package net.crowtheries.wirelessrouting;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import net.crowtheries.wirelessrouting.utils.Packet;
import net.crowtheries.wirelessrouting.utils.UDPSegment;

import ptolemy.actor.NoRoomException;
import ptolemy.actor.TypedAtomicActor;
import ptolemy.data.IntToken;
import ptolemy.data.StringToken;
import ptolemy.data.Token;
import ptolemy.data.RecordToken;
import ptolemy.data.expr.StringParameter;
import ptolemy.domains.wireless.kernel.WirelessIOPort;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;

public class FlowHandler extends TypedAtomicActor {
	public FlowHandler(CompositeEntity container, String name)
			throws IllegalActionException, NameDuplicationException {
		super(container, name);
		fromPacketHandler = new WirelessIOPort(this, "fromPacketHandler", true,
				false);
		toPacketHandler = new WirelessIOPort(this, "toPacketHandler", false,
				true);
		fromRoutesManager = new WirelessIOPort(this, "fromRoutesManager", true,
				false);
		toRoutesManager = new WirelessIOPort(this, "toRoutesManager", false,
				true);
		fromHighLevel = new WirelessIOPort(this, "fromHighLevel", true, false);
		toHighLevel = new WirelessIOPort(this, "toHighLevel", false, true);
		fromProtocolManager = new WirelessIOPort(this, "fromProtocolManager",
				true, false);
		toProtocolManager = new WirelessIOPort(this, "toProtocolManager",
				false, true);

		_packetCache = new HashMap<String, ArrayList<Packet>>();
	}

	// ////Private vars////
	private String _localIP;
	private HashMap<String, ArrayList<Packet>> _packetCache;

	// ports:
	public WirelessIOPort fromPacketHandler;
	public WirelessIOPort toPacketHandler;
	public WirelessIOPort toProtocolManager;
	public WirelessIOPort fromProtocolManager;
	public WirelessIOPort fromRoutesManager;
	public WirelessIOPort toRoutesManager;
	public WirelessIOPort fromHighLevel;
	public WirelessIOPort toHighLevel;

	public void initialize() throws IllegalActionException {
		StringParameter localip = (StringParameter) this.getContainer()
				.getAttribute("localIP");
		_localIP = localip.stringValue();
	}

	public void fire() throws IllegalActionException {
		// We start by getting the packets that come from outside
		if (fromPacketHandler.hasToken(0)) {
			String DSTIP;
			int DSTP;
			Packet packet = new Packet((RecordToken) fromPacketHandler.get(0));
			DSTIP = packet.getDSTIP();
			if (DSTIP.equals(_localIP) || DSTIP.equals("255.255.255.255")) {
				UDPSegment udpSeg = packet.getUDPSegment();

				DSTP = udpSeg.getDSTP();
				if (DSTP == 654) {
					toProtocolManager.send(0, packet.getPacketToken());
				} else {
					this.handleDataPacket(packet);
				}
			}
		}

		// We receive an IP for the next HOP:
		if (fromRoutesManager.hasToken(0)) {
			String NEXTHOP;
			String DSTIP;
			RecordToken message = (RecordToken) fromRoutesManager.get(0);
			NEXTHOP = ((StringToken) message.get("NEXTHOP")).stringValue();
			DSTIP = ((StringToken) message.get("DSTIP")).stringValue();
			System.out.println("**" + _localIP + ": DSTIP=" + DSTIP);
			for (Packet packet : _packetCache.get(DSTIP)) {
				packet.setDSTIP(NEXTHOP);
				toPacketHandler.send(0, packet.getPacketToken());
			}
			_packetCache.remove(DSTIP);

		}

		// we want to send data
		if (fromHighLevel.hasToken(0)) {
			Packet packet = new Packet((RecordToken) fromHighLevel.get(0));
			this.handleDataPacket(packet);
		}

		// we receive a protocole message to send , we just forward it
		if (fromProtocolManager.hasToken(0)) {
			toPacketHandler.send(0, fromProtocolManager.get(0));
		}
	}

	private void handleDataPacket(Packet packet) throws NoRoomException,
			IllegalActionException {
		UDPSegment udpSeg = packet.getUDPSegment();
		String DSTNODE = ((StringToken) udpSeg.getDATA().get("DSTNODE"))
				.stringValue();

		if (DSTNODE.equals(_localIP)) {
			toHighLevel.send(0, packet.getPacketToken());
		} else {
			// We start by sending the IP to RoutesManager to launch a request
			// process
			toRoutesManager.send(0, new StringToken(DSTNODE));

			// we cache the current packet with the DSTIP as key, to use it
			// later
			ArrayList<Packet> packetsList;

			// first, if the we don't have a packet for the DSTIP, we just put
			// it in
			if (!_packetCache.containsKey(DSTNODE)) {
				packetsList = new ArrayList<Packet>();
				packetsList.add(packet);
				_packetCache.put(DSTNODE, packetsList);
			}
			// else, we Check if we have a list of packets, else we create it
			else {
				packetsList = _packetCache.get(DSTNODE);
				packetsList.add(packet);
				_packetCache.put(DSTNODE, packetsList);
			}
		}

	}
}
